 #pragma once

#include "AES_128.h"
#include "MD5.h"
#include "Base64.h"
#ifndef  _WIN32
using UINT = unsigned int;
using INT64 = long long;
using UINT64 = unsigned long long;
using BYTE = unsigned char;
#endif
#include <string>
class DL_AES
{
	AES_128			_M_aes;
	BYTE*			_M_aes_buff = nullptr;
	UINT64			_M_aes_len = 0;
	BYTE*			_M_base64_buff = nullptr;
	UINT64			_M_base64_len = 0;
	inline bool _re_allocate(BYTE** p, UINT64& p_len, UINT64 size)
	{
		if (size > p_len)
		{
			BYTE* tmp = new BYTE[size];
			if (!tmp)
			{
				return false;
			}
			delete[] * p;
			*p = tmp;
			p_len = size;
		}
		return true;
	}

public:
	DL_AES() = default;
	~DL_AES()
	{
		delete _M_aes_buff;
		delete _M_base64_buff;
	}

	void setkey(char const * key)
	{
		BYTE m[16];
		MD5((BYTE*)key, strlen(key), m);
		_M_aes.set_key(m, m);
	}
	INT64 encrypt(const BYTE* _in, UINT64 _in_len, BYTE** out)
	{
		_re_allocate(&_M_aes_buff, _M_aes_len, ((_in_len >> 4) + 1) << 4);
		*out = _M_aes_buff;
		return _M_aes.cbc_encrypt(_in, _in_len, _M_aes_buff);
	}

	INT64 decrypt(const BYTE* _in, UINT64 _in_len, BYTE** out)
	{
		_re_allocate(&_M_aes_buff, _M_aes_len, _in_len);
		*out = _M_aes_buff;
		return _M_aes.cbc_decrypt(_in, _in_len, _M_aes_buff);
	}

	INT64 base64_encode(const BYTE* _in, UINT64 _in_len, char** out)
	{
		_re_allocate(&_M_base64_buff, _M_base64_len, _in_len << 1);
		*out = (char*)_M_base64_buff;
		return BASE64_ENC(_in, _in_len, (char*)_M_base64_buff);
	}

	INT64 base64_decode(const char* _in, BYTE** out)
	{
		_re_allocate(&_M_base64_buff, _M_base64_len, strlen(_in));
		*out = _M_base64_buff;
		return BASE64_DEC(_in, _M_base64_buff);
	}

	std::string url_encode(char const * str)
	{
		std::string tmp = "";
		size_t length = strlen(str);
		for (size_t i = 0; i < length; i++)
		{
			if (isalnum((unsigned char)str[i]) ||
				(str[i] == '-') ||
				(str[i] == '_') ||
				(str[i] == '.') ||
				(str[i] == '~'))
			{
				tmp += str[i];
			}
			else if (str[i] == ' ')
			{
				tmp += "+";
			}
			else
			{
				tmp += '%';
				tmp += _to_HEX((unsigned char)str[i] >> 4);
				tmp += _to_HEX((unsigned char)str[i] % 16);
			}
		}
		return tmp;
	}

	static char _to_hex(unsigned char x)
	{
		return  x > 9 ? x + 87 : x + 48;
	}

	static char _to_HEX(unsigned char x)
	{
		return  x > 9 ? x + 55 : x + 48;
	}
};